#region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

using Osh.Media;

namespace Osh.ZoomPlayer
{
	public class Timeline : Osh.Media.ITimeline
	{
		#region TimelineState
		public class TimelineState : Osh.Media.TimelineInfo
		{
			public TimelineState()
			{
			}

			public TimelineState(int durationSec, int positionSec)
			{
				// To avoid daylight saving time and other problems, just use the fixed date.
				this.StartTimeUtc = new DateTime(2007, 1, 1, 0, 0, 0, DateTimeKind.Local);
				this.CurrentTimeUtc = this.StartTimeUtc.Value.Add(TimeSpan.FromSeconds(positionSec));
				this.EndTimeUtc = this.StartTimeUtc.Value.Add(TimeSpan.FromSeconds(durationSec));
			}
		}
		#endregion TimelineState

		private ZoomPlayerApplication _player;
		private TimelineState _currentState;

		internal Timeline(ZoomPlayerApplication player)
		{
			_player = player;
			_player.Message += new EventHandler<MessageArgs>(Player_Message);
		}

		private void Player_Message(object sender, MessageArgs e)
		{
			if(e.Code == MessageCode.TimelineUpdate)
			{
				int currentPosition;
				int currentDuration;
				if(TryParseTimeTime(e.Message, out currentPosition, out currentDuration))
				{
					TimelineState prevState = _currentState;
					_currentState = new TimelineState(currentDuration, currentPosition);
					if(this.Change != null && TimelineState.HasChanged(_currentState, prevState))
						this.Change(this, new TimelineChangeArgs(_currentState));
				}
			}
			else
				return;
		}

		/// <summary>
		/// Parses playing position and duration from a string.
		/// </summary>
		/// <param name="timeStr">String with time in the following format: "00:00 / 1:52:27" or "1:44:43 / 1:52:27"</param>
		/// <param name="position">Position in seconds.</param>
		/// <param name="duration">Duration in seconds.</param>
		/// <returns></returns>
		private bool TryParseTimeTime(string timeStr, out int position, out int duration)
		{
			position = duration = 0;

			int slashPos = timeStr.IndexOf('/');
			if(slashPos == -1)
				return false;
			
			string positionStr = timeStr.Substring(0, slashPos - 1);
			string durationStr = timeStr.Substring(slashPos + 2);

			return TryParseTime(positionStr, out position) &&
				TryParseTime(durationStr, out duration);
		}
		
		private bool TryParseTime(string timeStr, out int seconds)
		{
			seconds = 0;

			if(timeStr.Length == 5)
				timeStr = "0:" + timeStr;

			TimeSpan timeSpan;
			if(!TimeSpan.TryParse(timeStr, out timeSpan))
				return false;

			seconds = (int)timeSpan.TotalSeconds;
			return true;
		}

		public TimelineInfo CurrentState
		{
			get
			{
				if((_player.State == PlayerState.Playing || _player.State == PlayerState.Paused) && _currentState != null)
					return _currentState;
				else
					return TimelineState.Empty;
			}
		}

		#region ITimeline
		public ITimelineInfo CaptureState()
		{
			return this.CurrentState;
		}

		public bool CanSeek
		{
			get { return true; }
		}

		public DateTime? SeekTo(DateTime position)
		{
			_player.SendCommand(CommandCode.SetPosition, ((int)position.TimeOfDay.TotalSeconds).ToString());
			return null;
		}

		public DateTime? SeekBy(int offset)
		{
			return this.SeekBy(TimeSpan.FromMilliseconds(offset));
		}

		public DateTime? SeekBy(TimeSpan offset)
		{
			DateTime? currentTime = this.CurrentState.CurrentTime;
			if(currentTime == null)
				return null;
			return this.SeekTo(currentTime.Value.Add(offset));
		}

		public event EventHandler<TimelineChangeArgs> Change;
		#endregion ITimeline
	}
}
